#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time   : 2021/7/9 22:32
# @Author : cjw
from fastapi import APIRouter, status, Form, File, UploadFile, HTTPException
from pydantic import BaseModel, EmailStr
from typing import Optional, Union, List

app04 = APIRouter()

"""响应模型"""


class UserBase(BaseModel):
    username: str
    email: EmailStr  # 会自动检查邮箱的格式, 用EmailStr 需要 pip install pydantic[email]
    mobile: str = '13000000000'
    address: Optional[str] = None
    full_name: Optional[str] = None


class UserIn(UserBase):
    password: str


class UserOut(UserBase):
    pass


users = {
    'user01': {
        'username': 'user01',
        'email': 'user01@qq.com',
        'password': '123456',
        'address': '明珠街120号',
        'full_name': 'this is full name'
    },
    'user02': {
        'username': 'user02',
        'email': 'user02@qq.com',
        'password': '654321',
        'address': '明珠街220号',
        'full_name': 'this is full name'
    }
}


@app04.post('/response_model', response_model=UserOut, name='响应模型')
def response_model(user: UserIn):
    """
    response_model_exclude_unset=True：表示默认值不包含在响应中，仅包含实际给的值
    :param user:
    :return:
    """
    print(user.username)
    return users['user01']


@app04.post('/response_model/attributes', response_model=Union[UserIn, UserOut], name='响应模型，联合类型')
def response_model_attributes(user: UserIn):
    """
    response_model_include: 列出需要在返回结果中包含的字段
    response_model_exclude：列出需要在返回结果中排除的字段
    :param user:
    :return:
    """
    del user.password  # 删除实例的属性也能正常返回
    return user


"""响应状态码"""


@app04.post('/status_code', status_code=status.HTTP_200_OK, name='响应状态码')
async def status_code():
    return {'status_code': status.HTTP_200_OK}


"""表单数据处理"""


@app04.post('/login', name='表单数据处理')
async def login(username: str = Form(...), password: str = Form(...)):
    """用Form类需要pip install python-multipart"""
    return {'username': username, 'password': password}


"""单文件、多文件上传及参数详解"""


@app04.post('/file', name='小文件上传')
async def file_(file: bytes = File(..., max_length=1 * 1024 * 1024)):
    """使用File类，文件内容会以bytes的形式读入内存，适合于小文件上传"""
    return {'file_size': len(file)}


@app04.post('/file_multi', name='多个小文件上传')
async def file_multi(files: List[bytes] = File(..., max_length=1 * 1024 * 1024)):
    file_dict = {}
    for i in range(len(files)):
        file_dict[f'第{i + 1}个文件'] = len(files[i])
    return file_dict


@app04.post('/upload_files', name='大文件上传')
async def upload_files(files: List[UploadFile] = File(...)):
    """
    使用UploadFile类的优势：
    1、文件存储在内存中，使用的内存达到阈值后，将被保存在磁盘中；
    2、适合于图片、视频大文件；
    3、可以获取上传的文件的元数据，如文件名，创建时间等；
    4、有文件对象的异步接口；
    5、上传的文件是Python文件对象，可以使用write(), read(), seek(), close()操作
    :param files:
    :return:
    """
    files_dict = {}
    for i in range(len(files)):
        # content = await files[i].read()
        files_dict[f'第{i + 1}个文件'] = {
            'filename': files[i].filename,
            'content_type': files[i].content_type,
            # 'content': content.title()
        }
    return files_dict


"""【见run.py】FastAPI项目的静态文件配置"""

"""路径操作配置"""


@app04.post(
    '/path_operation_configuration',
    response_model=UserOut,
    # tags=['Path', 'Operation', 'Configuration'],
    # summary='this is summary',
    description='这是一个描述',
    response_description='响应结果描述',
    # deprecated=True, # 接口废弃
    status_code=status.HTTP_200_OK,
    name='路径操作配置'
)
async def path_operation_configuration(user: UserIn):
    return user.dict()


"""【见run.py】FastAPI 应用的常见配置项"""

"""错误处理"""


@app04.get('/http_exception', summary='错误处理')
async def http_exception(city: str):
    if city != 'Beijing':
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
                            detail='城市没有找到',
                            headers={'X-Error': 'Error'})
    return {'city': city}


@app04.get('/http_exception/{city_id}', summary='错误处理')
async def override_http_exception(city_id: int):
    if city_id == 1:
        raise HTTPException(status_code=status.HTTP_418_IM_A_TEAPOT,
                            detail='城市没有找到',
                            headers={'X-Error': 'Error'})
    return {'city_id': city_id}
